#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "runtime.h"
#include "expr_parser.h"
#include "expr_lexer.h"
#include "expr_parser_types.h"

pcg_runtime_t *pcg_runtime_create() {
	pcg_runtime_t *rt = (pcg_runtime_t *) malloc(sizeof(pcg_runtime_t));
	rt->table = pcg_symtab_create();
	return rt;
}

void pcg_runtime_delete(pcg_runtime_t *rt) {
	pcg_symtab_delete(rt->table);
	free(rt);
}

void pcg_runtime_add_symbol(pcg_runtime_t *rt, char *name, int arity, pcg_runtime_fn_t functor) {
	pcg_symtab_insert_symbol(rt->table, name, arity, functor);
	
}
//void pcg_runtime_remove_symbol(char *name, int arity);

pcg_expr_t *pcg_runtime_expr_from_string(pcg_runtime_t *rt, char *s) {
	pcg_parse_parm_t pp;
	pcg_expr_t *result;
	pp.buf = s;
	pp.length = strlen(s);
	pp.pos = 0;
	pcg_lex_init(&pp.yyscanner);
	pcg_set_extra(&pp, pp.yyscanner);
	pcg_parse(&pp, pp.yyscanner);
	result = pp.result;
	pcg_lex_destroy(pp.yyscanner);
	return result;
}

pcg_expr_t *pcg_runtime_expr_from_fp(pcg_runtime_t *rt, FILE *fp) {
	return NULL;
}

double pcg_runtime_eval_expr(pcg_runtime_t *rt, pcg_expr_t *e) {
	pcg_sym_t *s;
	double v;

	switch (e->type) {
	case EXP_NUMBER:
		return e->lval.dval;
	case EXP_FUNC:
	case EXP_ID:
		s = pcg_symtab_lookup(rt->table, e->lval.sval, pcg_exprlist_length(e->args));	
		return ((pcg_runtime_fn_t)s->data)(e->args);
	case EXP_PLUS:
		return pcg_runtime_eval_expr(rt, e->left) + pcg_runtime_eval_expr(rt, e->right);
	case EXP_MINUS:
		return pcg_runtime_eval_expr(rt, e->left) - pcg_runtime_eval_expr(rt, e->right);
	case EXP_MULT:
		return pcg_runtime_eval_expr(rt, e->left) * pcg_runtime_eval_expr(rt, e->right);
	case EXP_DIV:
		v = pcg_runtime_eval_expr(rt, e->right);
		if (v == 0.0) { 
			return NAN;
		}
		return pcg_runtime_eval_expr(rt, e->left) / v;

	case EXP_LT:
		return (pcg_runtime_eval_expr(rt, e->left) < pcg_runtime_eval_expr(rt, e->right));
	case EXP_GT:
		return (pcg_runtime_eval_expr(rt, e->left) > pcg_runtime_eval_expr(rt, e->right));
	case EXP_AND:
		return ((pcg_runtime_eval_expr(rt, e->left) != 0.0) && (pcg_runtime_eval_expr(rt, e->right) != 0.0));
	case EXP_OR:
		return ((pcg_runtime_eval_expr(rt, e->left) != 0.0) || (pcg_runtime_eval_expr(rt, e->right) != 0.0));

	case EXP_POW:
		return pow(pcg_runtime_eval_expr(rt, e->left), pcg_runtime_eval_expr(rt, e->right));
	case EXP_UMINUS:
		return -pcg_runtime_eval_expr(rt, e->right);
	case EXP_NOT:
		return (pcg_runtime_eval_expr(rt, e->right) == 0.0);

	default:
		return 0;
	}
	
}

